package org.zjx.task.repository;

import org.zjx.task.AbstractChain;
import org.zjx.task.AbstractTask;

import java.io.File;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 任务仓库，用于管理和发现任务
 */
public class TaskChainRepository {
    private final Map<String, Class<? extends AbstractChain>> chainClasses = new HashMap<>();
    private final Map<String, AbstractChain> chainInstances = new HashMap<>();
    private final String chainPackage;

    /**
     * 构造任务仓库
     * @param chainPackage 任务包路径
     */
    public TaskChainRepository(String chainPackage) {
        this.chainPackage = chainPackage;
        discoverTasks();
    }

    /**
     * 动态发现任务类
     */
    private void discoverTasks() {
        try {
            // 使用ServiceLoader机制发现任务实现
            ServiceLoader<AbstractChain> loader = ServiceLoader.load(AbstractChain.class,
                    Thread.currentThread().getContextClassLoader());

            for (AbstractChain chain : loader) {
                chainClasses.put(chain.getChainId(), chain.getClass());
            }

            // 如果ServiceLoader没有找到，尝试扫描包
            if (chainClasses.isEmpty()) {
                scanPackageForTasks();
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to discover tasks", e);
        }
    }

    /**
     * 扫描包路径下的任务类
     */
    private void scanPackageForTasks() throws Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = chainPackage.replace('.', '/');
        URL resource = classLoader.getResource(path);

        if (resource != null) {
            // 解码 URL 编码的路径
            String decodedPath = URLDecoder.decode(resource.getFile(), StandardCharsets.UTF_8.name());
            File directory = new File(decodedPath);
            if (directory.exists()) {
                scanDirectory(classLoader, directory, path);
            }
        }
    }

    /**
     * 扫描目录中的任务类
     */
    private void scanDirectory(ClassLoader classLoader, File directory, String packagePath) throws Exception {
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    scanDirectory(classLoader, file, packagePath + "/" + file.getName());
                } else if (file.getName().endsWith(".class")) {
                    String className = packagePath + '/' + file.getName()
                            .replace(".class", "");
                    // 将路径中的斜杠替换为点
                    className = className.replace("/", ".");

                    Class<?> clazz = classLoader.loadClass(className);
                    if (AbstractTask.class.isAssignableFrom(clazz) && !clazz.isInterface()) {
                        AbstractTask task = (AbstractTask) clazz.getDeclaredConstructor().newInstance();
                        chainClasses.put(task.getTaskId(), (Class<? extends AbstractChain>) clazz);
                    }
                }
            }
        }
    }

    /**
     * 注册任务实例
     * @param chain 任务链实例
     */
    public void registerChain(AbstractChain chain) {
        chainInstances.put(chain.getChainId(), chain);
    }

    /**
     * 根据ID获取任务链实例
     * @param chainId 任务链ID
     * @return 任务链实例，如果不存在则创建新实例
     */
    public AbstractChain getChain(String chainId) {
        if (chainInstances.containsKey(chainId)) {
            return chainInstances.get(chainId);
        }
        return getChainByClass(chainId);
    }

    public AbstractChain getChainByClass(String chainId) {
        if (chainClasses.containsKey(chainId)) {
            try {
                AbstractChain chain = chainClasses.get(chainId).getDeclaredConstructor().newInstance();
                chainInstances.put(chainId, chain);
                return chain;
            } catch (Exception e) {
                throw new RuntimeException("Failed to create chain instance: " + chainId, e);
            }
        }

        return null;
    }


    /**
     * 获取所有任务ID
     * @return 任务ID列表
     */
    public List<String> getAllChainIds() {
        return new ArrayList<>(chainClasses.keySet());
    }
}
